﻿using UnityEngine;
using System.Collections.Generic;
using System;

namespace GameBasic
{
    public struct LongBits
    {
        public static List<int> tmpData;

        public ulong bits;
        internal int onBitCount;

        public void Set(ulong bits)
        {
            this.bits = bits;
            onBitCount = bits != 0 ? bits.BitCount() : 0;
        }

        public void Set(int index, bool on)
        {
            if (index < 0)
                return;

            ulong bit = 1ul << index;
            ulong result = on ? bits | bit : bits & (~bit);

            if (bits != result)
                onBitCount += on ? 1 : -1;

            bits = result;
        }

        public void ForeachOnBit(Action<int> act)
        {
            if (onBitCount == 0)
                return;

            int count = onBitCount;
            ulong b = bits;
            for (int i = 0; i < 64; i++)
            {
                if (b == 0)
                    break;

                // is bit on?
                if ((b & 1) != 0)
                {
                    act(i);
                    count--;
                    if (count == 0)
                        return;
                }

                b >>= 1;
            }
        }

        public void GetOnBits(ref List<int> results)
        {
            if (onBitCount == 0)
                return;

            if (results == null)
            {
                if (tmpData == null)
                    tmpData = new List<int>(16);
                else
                    tmpData.Clear();

                results = tmpData;
            }

            int count = onBitCount;
            ulong b = bits;
            for (int i = 0; i < 64; i++)
            {
                if (b == 0)
                    break;

                // is bit on?
                if ((b & 1) != 0)
                {
                    results.Add(i);
                    count--;
                    if (count == 0)
                        return;
                }

                b >>= 1;
            }
        }

        public int PickRandom(bool setOff)
        {
            if (onBitCount == 0)
                return -1;

            int result = 0;
            
            int rng = UnityEngine.Random.Range(0, onBitCount);
            for (int i = 0; i < 64; i++)
            {
                if (IsOn(i))
                {
                    if (result == rng)
                    {
                        result = i;
                        break;
                    }
                    else
                        result++;
                }
            }

            if (setOff)
                Set(result, false);

            return result;
        }

        public bool IsOn(int index)
        {
            return ((bits >> index) & 1ul) != 0;
        }

        public void All()
        {
            bits = ulong.MaxValue;
            onBitCount = 64;
        }

        public void Clear()
        {
            bits = 0;
            onBitCount = 0;
        }

        public static LongBits CreateRandom(int onBitsCount, int max)
        {
            ulong bits = (1ul << onBitsCount) - 1;

            // random swap
            for (int i = 0; i < onBitsCount; i++)
                bits = bits.Swap(i, UnityEngine.Random.Range(0, max));

            return new LongBits() {
                bits = bits,
                onBitCount = onBitsCount
            };
        }
    }
}